﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using lopet.data;
using Wintellect.PowerCollections;

namespace DiffisionWithDecomposition

{
    public class Simulation
    {
        private static Simulation _instance;
        public static Simulation Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new Simulation();
                }
                return _instance;
            }
        }
        private Simulation() { }

        public void NewSimulationInstance()
        {
            _instance = new Simulation();
        }

        public bool EndCondition = false;    //check this condition in the main loop
        private OrderedSet<Event> EventQueue;
        private long _simulationStopTime;
        private int _timeTick = 1;
        
        public Action DoOnTimeTick = null;
                
        /// <summary>
        /// Set Simulation.DoOnTimeTick to execute a function at each time tick.
        /// </summary>
        /// <param name="SimulationStopTime"></param>
        /// <param name="timeTick"></param>
        public void InitializeSimulation(long SimulationStopTime, int timeTick)
        {
            Time.Instance.SetTimeToZero();
            EndCondition = false;

            _timeTick = timeTick;

            EventQueue = new OrderedSet<Event>();
            _simulationStopTime = SimulationStopTime;
            
            var simStartEvent = new Event(0);
            EnqueueEvent(simStartEvent);

            EnqueueEvent(new Event(0, TimeTick));
            
            var simEndEvent = new Event(_simulationStopTime, () => { Console.WriteLine("Simulation over. Thank you."); EndCondition = true; });
            EnqueueEvent(simEndEvent);
        }

        private void TimeTick()
        {
            if (DoOnTimeTick != null)
            {
                DoOnTimeTick();
            }
            EnqueueEvent(new Event(Time.Instance.GetTimeAfterNanoSeconds(_timeTick), TimeTick));
        }

        public void EnqueueEvent(Event ev)
        {
            EventQueue.Add(ev);
        }

        public void DequeueEvent()
        {
            if (EventQueue.Count > 0)
            {
                var timeBefore = Time.Instance.Now;
                var ev = EventQueue.GetFirst();
                ev.ExecuteEvent();
                EventQueue.RemoveFirst();

                var timeAfter = Time.Instance.Now;
                //UpdateStatistics(timeBefore, timeAfter);
            }
        }
    }
}
